package es.unex.meigas.ww.core;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.geotools.data.DataStore;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.memory.MemoryDataStore;
import org.geotools.data.shapefile.dbf.DbaseFileException;
import org.geotools.data.shapefile.dbf.DbaseFileHeader;
import org.geotools.data.shapefile.dbf.DbaseFileReader;
import org.geotools.data.shapefile.dbf.DbaseFileWriter;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;

import com.vividsolutions.jts.geom.Geometry;

import es.unex.meigas.dataObjects.AbstractTable;
import es.unex.meigas.dataObjects.IRecord;
import es.unex.meigas.dataObjects.IRecordsetIterator;

public class WWTable extends AbstractTable {

	private String m_sName;
	private String m_sFilename;
	private int m_iCount;

	public void create(FeatureSource<SimpleFeatureType, SimpleFeature> featureSource) {

		try {
			m_BaseDataObject = featureSource;
			m_iCount = featureSource.getFeatures().size();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public void addRecord(Object[] values) {

		if (m_BaseDataObject instanceof FeatureStore) {
			try {
				FeatureStore<SimpleFeatureType, SimpleFeature> store = ((FeatureStore<SimpleFeatureType, SimpleFeature>) m_BaseDataObject);
				List<Object> attributes = new ArrayList<Object>();
				attributes.addAll(Arrays.asList(values));
				SimpleFeatureType ft = store.getSchema();
				FeatureCollection<SimpleFeatureType, SimpleFeature> collection
						= FeatureCollections.newCollection();

				SimpleFeature feature = SimpleFeatureBuilder.build(ft, attributes, SimpleFeatureBuilder.createDefaultFeatureId());
				collection.add(feature);
				store.addFeatures(collection);
				m_iCount++;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}


	}

	public void create(String sName,  String sFilename, Class<?>[] fields,
						String[] sFields) {

		try {
			m_sFilename = sFilename;
			m_sName = sName;
			SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
			builder.setName(sName);

			AttributeTypeBuilder attBuilder = new AttributeTypeBuilder();
			for (int i = 0; i < sFields.length; i++) {
				AttributeType type = attBuilder.binding(fields[0]).buildType();
				AttributeDescriptor descriptor = attBuilder.buildDescriptor(sFields[i], type);
				builder.add(descriptor);
			}
			DataStore mds = new MemoryDataStore();
			mds.createSchema(builder.buildFeatureType());

			create(mds.getFeatureSource(mds.getTypeNames()[0]));

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public IRecordsetIterator iterator() {

		if (m_BaseDataObject != null) {
				return new WWRecordsetIterator(m_BaseDataObject);
		} else {
			return null;
		}

	}

	public String getFieldName(int i) {

		if (m_BaseDataObject != null) {
			if (m_BaseDataObject instanceof FeatureSource){
				FeatureSource fs = (FeatureSource) m_BaseDataObject;
				try {
					SimpleFeatureType ft = (SimpleFeatureType) fs.getSchema();
					AttributeType at = ft.getType(i);
					return at.getName().getLocalPart();
				} catch (Exception e) {
					e.printStackTrace();
					return null;
				}
			}
			else if (m_BaseDataObject instanceof DbaseFileReader){
				DbaseFileReader dbf = (DbaseFileReader) m_BaseDataObject;
				return dbf.getHeader().getFieldName(i);
			}
		}

		return null;

	}

	public Class<?> getFieldType(int i) {

		if (m_BaseDataObject != null) {
			if (m_BaseDataObject instanceof FeatureSource){
				try {
					FeatureSource fs = (FeatureSource) m_BaseDataObject;
					SimpleFeatureType ft = (SimpleFeatureType) fs.getSchema();
					AttributeType at = ft.getType(i);
					return at.getBinding();
				} catch (Exception e) {
					e.printStackTrace();
					return null;
				}
			}
			else if (m_BaseDataObject instanceof DbaseFileReader){
				DbaseFileReader dbf = (DbaseFileReader) m_BaseDataObject;
				return getFieldTypeFromChar(dbf.getHeader().getFieldType(i));
			}
		}

		return null;

	}

	private Class getFieldTypeFromChar(char fieldType) {

		if (fieldType == 'N'){
			return Double.class;
		}
		else if (fieldType == 'D'){
			return Date.class;
		}
		else if (fieldType == 'L'){
			return Boolean.class;
		}
		else if (fieldType == 'C'){
			return String.class;
		}

		return Object.class;

	}

	public int getFieldCount() {

		if (m_BaseDataObject != null) {
			if (m_BaseDataObject instanceof FeatureSource){
				FeatureSource fs = (FeatureSource) m_BaseDataObject;
				try {
					SimpleFeatureType ft = (SimpleFeatureType) fs.getSchema();
					return ft.getAttributeCount();
				} catch (Exception e) {
					e.printStackTrace();
					return 0;
				}
			}
			else if (m_BaseDataObject instanceof DbaseFileReader){
				DbaseFileReader dbf = (DbaseFileReader) m_BaseDataObject;
				return dbf.getHeader().getNumFields();
			}
		}

		return 0;

	}

	public long getRecordCount() {

		return m_iCount;

	}

	public void close() {

		if (m_BaseDataObject instanceof DbaseFileReader){
			DbaseFileReader dbf = (DbaseFileReader) m_BaseDataObject;
			try {
				dbf.close();
			} catch (IOException e) {}
		}

	}

	public String getName() {

		return m_sName;
	}

	public void open() {}

	public void postProcess() {

    	if (m_BaseDataObject != null && m_BaseDataObject instanceof FeatureSource) {
    		DbaseFileWriter writer  = null;
    		try{
    			DbaseFileHeader header = createDbaseHeader();
    			WritableByteChannel out = new FileOutputStream(m_sFilename).getChannel();
    			Charset charset = Charset.defaultCharset();
    			writer = new DbaseFileWriter(header,out, charset);
    			IRecordsetIterator iter = this.iterator();
    			while (iter.hasNext()) {
    				IRecord record = iter.next();
    				writer.write(record.getValues());
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    		finally{
    			try {
    				writer.close();
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}

	}

    protected DbaseFileHeader createDbaseHeader() throws IOException, DbaseFileException {

    	if (m_BaseDataObject != null) {
    		if (m_BaseDataObject instanceof FeatureSource){
    			FeatureSource fs = (FeatureSource) m_BaseDataObject;
    			SimpleFeatureType featureType = (SimpleFeatureType) fs.getSchema();
    			DbaseFileHeader header = new DbaseFileHeader();
    			for (int i = 0, ii = featureType.getAttributeCount(); i < ii; i++) {
    				AttributeType type = featureType.getType(i);
    				Class colType = type.getBinding();
    				String colName = type.getName().getLocalPart();
    				int fieldLen = 255;

    				if((colType == Integer.class) || (colType == Short.class) || (colType == Byte.class)) {
    					header.addColumn(colName, 'N', Math.min(fieldLen,16), 0);
    				} else if((colType == Double.class) || (colType == Float.class) || colType == Number.class) {
    					int l = Math.min(fieldLen,33);
    					header.addColumn(colName, 'N', l, l / 2);
    				} else if(java.util.Date.class.isAssignableFrom(colType)) {
    					header.addColumn(colName, 'D', fieldLen, 0);
    				} else if (colType == Boolean.class) {
    					header.addColumn(colName, 'L', 1, 0);
    				} else if(CharSequence.class.isAssignableFrom(colType)) {
    					header.addColumn(colName, 'C', Math.min(254,fieldLen), 0);
    				} else if (Geometry.class.isAssignableFrom(colType)) {
    					continue;
    				} else {
    					throw new IOException("Unable to write : " + colType.getName());
    				}
    			}
    			return header;
    		}
    	}

    	return null;

    }

	public String getFilename() {

		return m_sFilename;

	}

	public void setName(String name) {

		m_sName = name;

	}




}
